use im::*;

mod size;
pub use size::*;

mod position;
pub use position::*;

mod position_offset;
pub use position_offset::*;

pub struct Grid<T>
where
    T: std::clone::Clone,
{
    pub size: Size,
    // tiles: Vec<T>,
    tiles: HashMap<Position, T>,
}

impl<T> Grid<T>
where
    T: std::clone::Clone,
{
    pub fn empty(size: Size) -> Self {
        Grid {
            size,
            tiles: HashMap::new(),
        }
    }

    pub fn at(&self, position: &Position) -> Option<&T> {
        self.tiles.get(position)
    }

    pub fn set(&self, pos: &Position, tile: T) -> Self {
        let mut new_tiles = self.tiles.clone();
        new_tiles.insert(*pos, tile);
        Grid {
            size: self.size,
            tiles: new_tiles,
        }
    }

    pub fn iter(&self) -> GridIterator<T> {
        GridIterator {
            grid: self,
            position_iterator: self.size.iter_positions(),
        }
    }
}

pub struct GridIterator<'a, T>
where
    T: std::clone::Clone,
{
    grid: &'a Grid<T>,
    position_iterator: PositionsOfSizeIterator,
}

impl<'a, T> Iterator for GridIterator<'a, T>
where
    T: std::clone::Clone,
{
    type Item = (Position, &'a T);

    fn next(&mut self) -> Option<Self::Item> {
        self.position_iterator.next().and_then(|pos| {
            self.grid
                .at(&pos)
                .map(|tile| (pos, tile))
                .or_else(|| self.next())
        })
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    #[test]
    fn test_grid_iterator() {
        let size = size(3, 3);
        let g: Grid<Size> = Grid::empty(size)
            .set(&Position::new(size, 1, 1), size)
            .set(&Position::new(size, 1, 2), size)
            .set(&Position::new(size, 1, 2), size);

        let count = g.iter().count();

        assert_eq!(2, count);
    }
}
